/*
 * 智能门禁系统 - ESP32设备端代码 (精简测试版)
 * 版本: V0.16.0.2
 * 功能: WiFi连接、WebSocket通信 (用于连接测试)
 * 作者: 课程设计项目
 * 日期: 2025-07-14
 */

 #include <WiFi.h>
 #include <WebSocketsClient.h>
 #include <ArduinoJson.h>
 #include <SPI.h>
 #include <MFRC522.h>
 #include <Preferences.h> // 引入NVS库
 #include <Wire.h>        // I2C库
 #include <Adafruit_GFX.h>
 #include <Adafruit_SSD1306.h> // OLED显示库
 #include <Adafruit_NeoPixel.h> // RGB LED库
 
 // 包含您的本地配置文件
 // 请确保您已将 config_template.h 复制为 config.h 并填入正确信息
 #include "config.h"

 // ==================== 函数声明 ====================
 void initializeHardware();
 void initializeOLED();
 void initializeRgbLed();
 void displayMessage(String message, bool temporary);
 void updateOLEDDisplay();
 void triggerRelay();
 void checkRelayStatus();
 void setLedColor(bool green, bool red);
 void setRgbColor(uint32_t color);
 void updateRgbLed();
 void checkManualButton();
 void testRgbLed();
 
 // ==================== 全局变量 ====================
 WebSocketsClient webSocket;
 Preferences preferences; // 创建Preferences对象

 // OLED显示对象
 #if ENABLE_OLED
 Adafruit_SSD1306 display(OLED_WIDTH, OLED_HEIGHT, &Wire, -1);
 #endif

 // RGB LED对象
 #if ENABLE_RGB_LED
 Adafruit_NeoPixel rgbLed(RGB_LED_COUNT, RGB_LED_PIN, NEO_GRB + NEO_KHZ800);
 #endif

 // 设备状态
 bool isConnected = false;
 bool isRegistered = false; // 用于跟踪设备是否已注册
 unsigned long lastHeartbeat = 0;

 // NFC/RFID 相关变量
 MFRC522 mfrc522(NFC_SS_PIN, NFC_RST_PIN);  // 创建MFRC522实例

 // NFC状态管理
 bool nfcInitialized = false;
 String lastCardUID = "";
 unsigned long lastCardTime = 0;

 // 继电器状态管理
 bool relayActive = false;
 unsigned long relayStartTime = 0;

 // OLED显示管理
 String currentMessage = "";
 unsigned long messageStartTime = 0;
 bool messageTemporary = false;

 // RGB LED状态管理
 uint32_t currentRgbColor = RGB_COLOR_YELLOW; // 默认黄色（待机）
 unsigned long lastRgbUpdate = 0;
 
 // ==================== 初始化函数 ====================
 void setup() {
   Serial.begin(SERIAL_BAUD_RATE);
   Serial.println("智能门禁系统启动中 (WebSocket测试模式)...");
 
   // 初始化NVS并读取注册状态
   preferences.begin("device_state", false); // "device_state"是命名空间
   isRegistered = preferences.getBool("isRegistered", false);
   preferences.end();
 
   if (isRegistered) {
     Serial.println("设备状态：已注册");
   } else {
     Serial.println("设备状态：未注册");
   }

   // 初始化硬件
   initializeHardware();

   // 初始化OLED显示
   initializeOLED();

   // 初始化RGB LED
   initializeRgbLed();

   // 连接WiFi
   connectToWiFi();

   // 初始化WebSocket连接
   initializeWebSocket();

   // 初始化NFC模块
   initializeNFC();

   // 显示设备就绪信息
   displayMessage(MSG_DEVICE_READY, false);

   // 可选：启动时测试RGB LED（取消注释以启用）
   // testRgbLed();

   // 设置RGB LED为黄色（待机状态）
   setRgbColor(RGB_COLOR_YELLOW);

   Serial.println("设备初始化完成！等待连接...");
 }
 
 // ==================== 主循环 ====================
 void loop() {
   // 处理WebSocket事件
   webSocket.loop();

   // 检查并发送心跳包 (非阻塞)
   if (isConnected && (millis() - lastHeartbeat > HEARTBEAT_INTERVAL)) {
     sendHeartbeat();
     lastHeartbeat = millis();
   }

   // 检查NFC卡片 (非阻塞)
   checkNFCCard();

   // 检查继电器状态 (非阻塞)
   checkRelayStatus();

   // 检查手动按钮 (非阻塞)
   checkManualButton();

   // 更新OLED显示 (非阻塞)
   updateOLEDDisplay();

   // 更新RGB LED (非阻塞)
   updateRgbLed();

   // 检查WiFi连接状态 (非阻塞)
   if (WiFi.status() != WL_CONNECTED) {
     Serial.println("WiFi连接断开，尝试重连...");
     connectToWiFi();
   }
 }
 
 // ==================== WiFi连接 ====================
 void connectToWiFi() {
   Serial.print("正在连接WiFi: ");
   Serial.println(WIFI_SSID);

   // 显示WiFi连接状态
   displayMessage(MSG_WIFI_CONNECTING, false);

   // 设置RGB LED为蓝色（系统状态）
   setRgbColor(RGB_COLOR_BLUE);

   WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

   int attempts = 0;
   while (WiFi.status() != WL_CONNECTED && attempts < (WIFI_CONNECT_TIMEOUT * 2)) {
     delay(500); // 短暂阻塞是可接受的，因为它只在启动或断线时发生
     Serial.print(".");
     attempts++;
   }

   if (WiFi.status() == WL_CONNECTED) {
     Serial.println();
     Serial.println("WiFi连接成功！");
     Serial.print("IP地址: ");
     Serial.println(WiFi.localIP());

     // 显示WiFi连接成功
     displayMessage(MSG_WIFI_CONNECTED, true);
   } else {
     Serial.println();
     Serial.println("WiFi连接失败！将在一分钟后重试...");

     // 显示WiFi连接失败
     displayMessage(MSG_WIFI_FAILED, false);

     delay(60000); // 连接失败则等待较长时间
     ESP.restart();
   }
 }
 
 // ==================== WebSocket通信 ====================
 void initializeWebSocket() {
   // 显示WebSocket连接状态
   displayMessage(MSG_WS_CONNECTING, false);

   // 构造包含设备密钥的完整WebSocket路径
   String full_websocket_path = String(WEBSOCKET_PATH) + "?device_key=" + String(DEVICE_KEY);

   Serial.print("构造WebSocket连接地址: ");
   Serial.println(full_websocket_path);

   webSocket.begin(WEBSOCKET_SERVER, WEBSOCKET_PORT, full_websocket_path.c_str(), "arduino-websockets");
   webSocket.onEvent(webSocketEvent);
   webSocket.setReconnectInterval(WS_RECONNECT_INTERVAL);

   Serial.println("WebSocket客户端初始化完成");
 }
 
 void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
   switch(type) {
     case WStype_DISCONNECTED:
       Serial.println("WebSocket断开连接");
       isConnected = false;
       displayMessage(MSG_WS_DISCONNECTED, false);
       break;

     case WStype_CONNECTED:
       Serial.printf("WebSocket连接成功: %s\n", payload);
       isConnected = true;
       displayMessage(MSG_WS_CONNECTED, true);
       // 只有在首次连接时才发送注册信息
       if (!isRegistered) {
         sendDeviceRegistration();
         isRegistered = true; // 标记为已注册
       } else {
         Serial.println("WebSocket重新连接成功");
       }
       break;
       
     case WStype_TEXT:
       Serial.printf("收到服务器消息: %s\n", payload);
       handleWebSocketMessage((char*)payload);
       break;
       
     case WStype_ERROR:
       Serial.printf("WebSocket错误: %s\n", payload);
       break;
       
     default:
       break;
   }
 }
 
 void sendDeviceRegistration() {
   DynamicJsonDocument doc(JSON_BUFFER_SIZE);
   doc["event"] = EVENT_DEVICE_REGISTER;
   doc["device_id"] = DEVICE_ID;
   doc["device_name"] = DEVICE_NAME;
   doc["device_key"] = DEVICE_KEY;
   doc["ip_address"] = WiFi.localIP().toString();
   doc["firmware_version"] = FIRMWARE_VERSION;
   
   String message;
   serializeJson(doc, message);
   webSocket.sendTXT(message);
   
   Serial.println("发送设备注册信息");
 }
 
 void sendHeartbeat() {
   if (!isConnected) return;
   
   DynamicJsonDocument doc(512);
   doc["event"] = EVENT_HEARTBEAT;
   doc["device_id"] = DEVICE_ID;
   doc["status"] = "online";
   doc["timestamp"] = millis();
   
   String message;
   serializeJson(doc, message);
   webSocket.sendTXT(message);
   
   Serial.println("发送心跳包");
 }
 
 void handleWebSocketMessage(const char* message) {
   DynamicJsonDocument doc(JSON_BUFFER_SIZE);
   DeserializationError error = deserializeJson(doc, message);
 
   if (error) {
     Serial.print(F("deserializeJson() failed: "));
     Serial.println(error.c_str());
     return;
   }
   
   // 统一使用 'event' 协议，并增加健壮性检查
   const char* event = doc["event"];
   
   if (event == nullptr) {
       Serial.println("收到的消息中缺少 'event' 字段");
       return;
   }
 
   if (strcmp(event, EVENT_CONNECTION_ESTABLISHED) == 0) {
     Serial.println("连接已确认");
   } else if (strcmp(event, EVENT_DEVICE_REGISTER_ACK) == 0) {
     Serial.println("设备注册已由服务器确认");
     // 将注册状态写入NVS
     preferences.begin("device_state", false);
     preferences.putBool("isRegistered", true);
     preferences.end();
     Serial.println("注册状态已保存到NVS");
   } else if (strcmp(event, EVENT_HEARTBEAT_RESPONSE) == 0) {
     Serial.println("收到心跳响应");
   } else if (strcmp(event, EVENT_DEVICE_RESTART) == 0) {
     Serial.println("收到重启命令，将在3秒后重启...");
     delay(3000);
     ESP.restart();
   } else if (strcmp(event, EVENT_STATUS_REQUEST) == 0) {
     Serial.println("收到状态查询请求");
     sendDeviceStatus();
   } else if (strcmp(event, EVENT_ACCESS_GRANTED) == 0) {
     const char* user = doc["user"] | "Unknown User";
     Serial.print("访问已授权，欢迎: ");
     Serial.println(user);

     // 触发继电器开门
     triggerRelay();

     // 点亮绿色LED
     setLedColor(true, false); // 绿灯亮，红灯灭

     // 设置RGB LED为绿色（门开启）
     setRgbColor(RGB_COLOR_GREEN);

     // 显示访问授权信息
     String welcomeMsg = String(MSG_ACCESS_GRANTED) + "\n" + String(user);
     displayMessage(welcomeMsg, true);

   } else if (strcmp(event, EVENT_ACCESS_DENIED) == 0) {
     const char* reason = doc["reason"] | "Unknown Reason";
     Serial.print("访问被拒绝，原因: ");
     Serial.println(reason);

     // 点亮红色LED
     setLedColor(false, true); // 绿灯灭，红灯亮

     // 设置RGB LED为红色（验证失败）
     setRgbColor(RGB_COLOR_RED);

     // 显示访问拒绝信息
     String denyMsg = String(MSG_ACCESS_DENIED) + "\n" + String(reason);
     displayMessage(denyMsg, true);
   } else {
     Serial.print("收到未知类型的事件: ");
     Serial.println(event);
   }
 }
 
 void sendDeviceStatus() {
   DynamicJsonDocument doc(512);
   doc["event"] = EVENT_DEVICE_STATUS;
   doc["device_id"] = DEVICE_ID;
   doc["status"] = "online";
   doc["wifi_rssi"] = WiFi.RSSI();
   doc["free_heap"] = ESP.getFreeHeap();
   doc["uptime"] = millis();
   
   String message;
   serializeJson(doc, message);
   webSocket.sendTXT(message);
   Serial.println("发送设备状态信息");
 }
 
 // ==================== NFC/RFID 功能 ====================
 void initializeNFC() {
   if (!ENABLE_NFC) {
     Serial.println("NFC功能已禁用");
     return;
   }
   
   Serial.println("初始化NFC模块...");
   
   // 初始化SPI总线 - 使用自定义引脚
   SPI.begin(18, 12, 13, 5);  // SCK=18, MISO=12, MOSI=13, SS=5
   
   // 初始化MFRC522
   mfrc522.PCD_Init();
   
   // 检查MFRC522连接
   byte version = mfrc522.PCD_ReadRegister(mfrc522.VersionReg);
   if (version == 0x00 || version == 0xFF) {
     Serial.println("NFC模块连接失败！请检查接线。");
     nfcInitialized = false;
     return;
   }
   
   Serial.print("MFRC522固件版本: 0x");
   Serial.println(version, HEX);
   Serial.println("NFC模块初始化成功！");
   nfcInitialized = true;
   
   // 设置天线增益（可选）
   mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_max);
 }
 
 void checkNFCCard() {
   if (!nfcInitialized || !ENABLE_NFC) {
     return;
   }
   
   // 检查是否有新卡片
   if (!mfrc522.PICC_IsNewCardPresent()) {
     return;
   }
   
   // 选择卡片
   if (!mfrc522.PICC_ReadCardSerial()) {
     return;
   }
   
   // 获取卡片UID
   String cardUID = getCardUID();
   
   // 防重复读取检查
   if (cardUID == lastCardUID && (millis() - lastCardTime) < CARD_DUPLICATE_TIMEOUT) {
     // 停止卡片通信
     mfrc522.PICC_HaltA();
     mfrc522.PCD_StopCrypto1();
     return;
   }
   
   // 更新最后读取信息
   lastCardUID = cardUID;
   lastCardTime = millis();
   
   Serial.print("NFC card detected, UID: ");
   Serial.println(cardUID);

   // 显示NFC检测信息
   String nfcMsg = String(MSG_NFC_DETECTED) + "\n" + cardUID;
   displayMessage(nfcMsg, true);

   // 发送NFC事件到服务器
   sendNFCEvent(cardUID);
   
   // 停止卡片通信
   mfrc522.PICC_HaltA();
   mfrc522.PCD_StopCrypto1();
 }
 
 String getCardUID() {
   String uid = "";
   for (byte i = 0; i < mfrc522.uid.size; i++) {
     if (mfrc522.uid.uidByte[i] < 0x10) {
       uid += "0";
     }
     uid += String(mfrc522.uid.uidByte[i], HEX);
   }
   uid.toUpperCase();
   return uid;
 }
 
 void sendNFCEvent(String cardUID) {
   if (!isConnected) {
     Serial.println("WebSocket未连接，无法发送NFC事件");
     return;
   }
   
   DynamicJsonDocument doc(JSON_BUFFER_SIZE);
   doc["event"] = EVENT_NFC_SCAN;
   doc["device_id"] = DEVICE_ID;
   doc["card_id"] = cardUID;
   doc["timestamp"] = millis();
   doc["card_type"] = "MIFARE_CLASSIC";  // 可以后续扩展检测卡片类型
   
   String message;
   serializeJson(doc, message);
   webSocket.sendTXT(message);
   
   Serial.println("发送NFC事件到服务器");
   Serial.print("卡片UID: ");
   Serial.println(cardUID);
 }

 // ==================== 硬件初始化函数 ====================
 void initializeHardware() {
   Serial.println("初始化硬件引脚...");

   // 初始化LED引脚
   #if ENABLE_LED_INDICATOR
   pinMode(LED_GREEN_PIN, OUTPUT);
   pinMode(LED_RED_PIN, OUTPUT);
   digitalWrite(LED_GREEN_PIN, LOW);
   digitalWrite(LED_RED_PIN, LOW);
   Serial.println("LED引脚初始化完成");
   #endif

   // 初始化继电器引脚
   #if ENABLE_RELAY
   pinMode(RELAY_PIN, OUTPUT);
   digitalWrite(RELAY_PIN, RELAY_ACTIVE_HIGH ? LOW : HIGH); // 初始状态为关闭
   Serial.println("继电器引脚初始化完成");
   #endif

   // 初始化按钮引脚
   #if ENABLE_MANUAL_BUTTON
   pinMode(BUTTON_PIN, INPUT_PULLUP);
   Serial.println("按钮引脚初始化完成");
   #endif

   Serial.println("硬件初始化完成");
 }

 // ==================== OLED显示函数 ====================
 void initializeOLED() {
   #if ENABLE_OLED
   Serial.println("初始化OLED显示...");

   // 初始化I2C
   Wire.begin(OLED_SDA_PIN, OLED_SCL_PIN);

   // 初始化OLED显示器
   if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) {
     Serial.println("OLED初始化失败!");
     return;
   }

   // 清空显示缓冲区
   display.clearDisplay();

   // 设置文本参数
   display.setTextSize(OLED_FONT_SIZE);
   display.setTextColor(SSD1306_WHITE);
   display.setCursor(0, 0);

   // 显示设备名称
   display.println(DEVICE_NAME);
   display.println("Starting...");
   display.display();

   Serial.println("OLED初始化完成");
   #endif
 }

 void displayMessage(String message, bool temporary) {
   #if ENABLE_OLED
   currentMessage = message;
   messageTemporary = temporary;
   messageStartTime = millis();

   // 立即更新显示
   display.clearDisplay();
   display.setCursor(0, 0);
   display.setTextSize(1);
   display.setTextColor(SSD1306_WHITE);

   // 显示设备名称
   display.println(DEVICE_NAME);
   display.println("================");

   // 显示消息内容
   display.println(message);

   // 显示连接状态
   display.println("----------------");
   if (isConnected) {
     display.println("Status: Connected");
   } else {
     display.println("Status: Disconnected");
   }

   display.display();

   Serial.print("OLED显示: ");
   Serial.println(message);
   #endif
 }

 void updateOLEDDisplay() {
   #if ENABLE_OLED
   // 检查临时消息是否需要清除
   if (messageTemporary && (millis() - messageStartTime > OLED_MESSAGE_TIMEOUT)) {
     displayMessage(MSG_DEVICE_READY, false);

     // 如果不是在开门状态（继电器未激活），恢复RGB LED为黄色
     #if ENABLE_RGB_LED
     if (!relayActive && currentRgbColor == RGB_COLOR_RED) {
       setRgbColor(RGB_COLOR_YELLOW);
     }
     #endif
   }
   #endif
 }

 // ==================== 继电器控制函数 ====================
 void triggerRelay() {
   #if ENABLE_RELAY
   Serial.println("触发继电器开门");

   // 激活继电器
   digitalWrite(RELAY_PIN, RELAY_ACTIVE_HIGH ? HIGH : LOW);
   relayActive = true;
   relayStartTime = millis();

   Serial.print("继电器将在 ");
   Serial.print(RELAY_TRIGGER_DURATION);
   Serial.println(" 毫秒后自动关闭");
   #endif
 }

 void checkRelayStatus() {
   #if ENABLE_RELAY
   // 检查继电器是否需要关闭
   if (relayActive && (millis() - relayStartTime > RELAY_TRIGGER_DURATION)) {
     digitalWrite(RELAY_PIN, RELAY_ACTIVE_HIGH ? LOW : HIGH);
     relayActive = false;
     Serial.println("继电器自动关闭");

     // 关闭LED指示
     setLedColor(false, false);

     // 设置RGB LED为黄色（门关闭/待机）
     setRgbColor(RGB_COLOR_YELLOW);

     // 显示门已关闭
     displayMessage(MSG_DOOR_CLOSED, true);
   }
   #endif
 }

 // ==================== LED控制函数 ====================
 void setLedColor(bool green, bool red) {
   #if ENABLE_LED_INDICATOR
   digitalWrite(LED_GREEN_PIN, green ? HIGH : LOW);
   digitalWrite(LED_RED_PIN, red ? HIGH : LOW);

   if (green && !red) {
     Serial.println("LED: 绿灯亮");
   } else if (!green && red) {
     Serial.println("LED: 红灯亮");
   } else if (!green && !red) {
     Serial.println("LED: 全部关闭");
   } else {
     Serial.println("LED: 绿红同时亮");
   }
   #endif
 }

 // ==================== 手动按钮处理函数 ====================
 void checkManualButton() {
   #if ENABLE_MANUAL_BUTTON
   static unsigned long lastButtonPress = 0;
   static bool lastButtonState = HIGH;

   bool currentButtonState = digitalRead(BUTTON_PIN);

   // 检测按钮按下（下降沿）
   if (lastButtonState == HIGH && currentButtonState == LOW) {
     if (millis() - lastButtonPress > BUTTON_DEBOUNCE_DELAY) {
       Serial.println("手动开门按钮被按下");

       // 触发继电器
       triggerRelay();

       // 设置RGB LED为绿色（门开启）
       setRgbColor(RGB_COLOR_GREEN);

       // 显示手动开门信息
       displayMessage(MSG_MANUAL_OPEN, true);

       lastButtonPress = millis();
     }
   }

   lastButtonState = currentButtonState;
   #endif
 }

 // ==================== RGB LED控制函数 ====================
 void initializeRgbLed() {
   #if ENABLE_RGB_LED
   Serial.println("初始化RGB LED...");

   // 初始化RGB LED
   rgbLed.begin();
   rgbLed.setBrightness(RGB_LED_BRIGHTNESS);

   // 设置初始颜色为黄色（待机状态）
   setRgbColor(RGB_COLOR_YELLOW);

   Serial.println("RGB LED初始化完成");
   #endif
 }

 void setRgbColor(uint32_t color) {
   #if ENABLE_RGB_LED
   // 输出调试信息（在设置前）
   Serial.print("设置RGB LED颜色: 0x");
   Serial.print(color, HEX);
   Serial.print(" -> ");

   currentRgbColor = color;

   // 立即更新RGB LED颜色
   rgbLed.setPixelColor(0, color);
   rgbLed.show();

   // 输出调试信息
   if (color == RGB_COLOR_RED) {
     Serial.println("红色 (验证失败)");
   } else if (color == RGB_COLOR_GREEN) {
     Serial.println("绿色 (门开启)");
   } else if (color == RGB_COLOR_YELLOW) {
     Serial.println("黄色 (门关闭/待机)");
   } else if (color == RGB_COLOR_BLUE) {
     Serial.println("蓝色 (系统状态)");
   } else if (color == RGB_COLOR_OFF) {
     Serial.println("关闭");
   } else {
     Serial.println("自定义颜色");
   }

   // 添加短暂延迟确保颜色设置生效
   delay(10);
   #endif
 }

 void updateRgbLed() {
   #if ENABLE_RGB_LED
   // 定期更新RGB LED（防止意外关闭）
   if (millis() - lastRgbUpdate > RGB_UPDATE_INTERVAL) {
     rgbLed.setPixelColor(0, currentRgbColor);
     rgbLed.show();
     lastRgbUpdate = millis();
   }
   #endif
 }

 // RGB LED测试函数（可选）
 void testRgbLed() {
   #if ENABLE_RGB_LED
   Serial.println("开始RGB LED测试...");

   // 测试红色
   setRgbColor(RGB_COLOR_RED);
   delay(1000);

   // 测试绿色
   setRgbColor(RGB_COLOR_GREEN);
   delay(1000);

   // 测试黄色
   setRgbColor(RGB_COLOR_YELLOW);
   delay(1000);

   // 测试蓝色
   setRgbColor(RGB_COLOR_BLUE);
   delay(1000);

   // 回到待机状态
   setRgbColor(RGB_COLOR_YELLOW);

   Serial.println("RGB LED测试完成");
   #endif
 }
 